package module;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import core.AbstractCard;
import core.TextAttribute;

/**
 * Loads and caches sets of AbstractCards from XML files using the CardParser.
 */
public class SetLoader
{
	/**
	 * Retrieves an AbstractCard prototype from the given set.  If setName is null,
	 * returns FACE_DOWN_CARD.
	 * @param setName the name of the card set that contains the desired card
	 * @param cardName the name of the card
	 * @return the card desired, or null if it is not found in the card sets
	 */
	public static AbstractCard getAbstractCard(String setName, String cardName) {
		return setName == null ? FACE_DOWN_CARD : getSet(setName).get(cardName);
	}

	/**
	 * Returns a set of AbstractCards.  If the set has not already been loaded,
	 * uses the CardParser to load the set from the card sets stored in the /sets
	 * directory.
	 * @param set the name of the AbstractCard set
	 */
	public static Map<String, AbstractCard> getSet(String setName)
	{
		String realName = core.GameParticipantRegistry.get().getModule() + "/" + setName;
		Map<String, AbstractCard> set = sets.get(realName);
		if (set == null)
			try {
				if ((set = CardParser.parse(setName)) != null)
					sets.put(realName, set);
			} catch (nu.xom.ParsingException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		return set;
	}

	public static final AbstractCard FACE_DOWN_CARD = new AbstractCard(null, null, null);

	private static final Map<String, Map<String, AbstractCard>> sets = new HashMap<String, Map<String, AbstractCard>>();

	/**
	 * Utility class to parse XML files representing sets of AbstractCards.
	 * XML files that specify Card sets must conform to the following structure:
	 * <p>
	 * - The root element must be "cards"<br>
	 * - Each subelement must be have the name "card" and must, at a minimum, 
	 * specify a "name" attribute.<br>
	 * - Please see the example card sets in the /sets directory for examples.
	 */
	private static class CardParser
	{
		/**
		 * Loads a set of cards from an XML file.
		 * @param set the name of the XML file (without the .xml extension) (located in the sets/ directory)
		 * @throws ParsingException if the XML file fails to parse
		 * @throws IOException if the XML file is not found
		 * @return a mapping of the names of the cards to the AbstractCards specified in the XML file
		 */
		private static Map<String, AbstractCard> parse(String set) throws nu.xom.ParsingException, IOException
		{
			nu.xom.Element cardElement, attributeElement,
			  root = new nu.xom.Builder().build(new File("sets/" + core.GameParticipantRegistry.get().getModule() + "/" + set + ".xml")).getRootElement();
			AbstractCard card;
			Map<String, AbstractCard> cards = new HashMap<String, AbstractCard>();
			if (root.getQualifiedName().equals("cards"))
				for (int i = 0; i < root.getChildElements().size(); ++i)
					if ((cardElement = root.getChildElements().get(i)).getQualifiedName().equals("card")) {
						cards.put(cardElement.getAttributeValue("name"), (card = new AbstractCard(set, cardElement.getAttributeValue("name"), cardElement.getAttributeValue("image"))));
						for (int j = 0; j < cardElement.getChildElements().size(); ++j)
							if ((attributeElement = cardElement.getChildElements().get(j)).getQualifiedName().equals("attribute"))
								card.addAttribute(new TextAttribute(attributeElement.getAttributeValue("name"), attributeElement.getAttributeValue("value")));
					}
			return cards;
		}
	}
}